
# include "dfxOggProvider.h"
# include "dfxLog.h"
# include "dfxFileReader.h"

# pragma comment (lib, "libogg/libogg_static.lib")
# pragma comment (lib, "libvorbis/libvorbis_static.lib")
# pragma comment (lib, "libvorbis/libvorbisfile_static.lib")

namespace DFX
{
    OggProvider::OggProvider()
        : mStream(NULL), mOGRawPosition(0)
    {
    }

    OggProvider::~OggProvider()
    {
        Destroy();
    }

    DFXRESULT OggProvider::Load(FileBase *file)
    {
        if (mStream)
            Destroy();

        UInt8 *data = new UInt8[static_cast<UInt32>(file->GetSize())];
        memcpy(data, file->GetData(), static_cast<UInt32>(file->GetSize()));       

        mOggFile = new MemoryFile();
        mOggFile->CreateFromBuffer(data, file->GetSize());

        mStream = new FileReader(mOggFile);
        ov_callbacks callbacks;

        callbacks.read_func = OGG_ReadFunc;
        callbacks.seek_func = OGG_SeekFunc;
        callbacks.close_func = OGG_CloseFunc;
        callbacks.tell_func = OGG_TellFunc;

        if (ov_open_callbacks(this, &mVorbisFile, NULL, -1, callbacks) < 0)
        {
            DFX_ERROR("Unable to open Ogg stream: %s", mStream->GetFileName());
            return DFX_LOADFAILED;
        }

        vorbis_info *vinfo = ov_info(&mVorbisFile, -1);

        mChannels = vinfo->channels;
        mSamplesPerSec = vinfo->rate;
        mBitsPerSample = 16;
        mWaveFormat = WAVE_FORMAT_STEREO16;

        return DFX_SUCCESS;
    }

    Int32 OggProvider::ReadFromFile(UInt32 size, UInt32 bytesRead)
    {
        return (Int32)ov_read(&mVorbisFile, (char*)&mWaveData[0] + bytesRead, 
            size - bytesRead, 0, mBitsPerSample >> 3, 1, &mCurrentSection);
    }

    void OggProvider::Seek(Float32 time)
    {
        ov_time_seek(&mVorbisFile, time);
    }

    DFXRESULT OggProvider::Destroy()
    {
        if (mStream)
        {
            ov_clear(&mVorbisFile);

            SAFE_DELETE(mOggFile);
            SAFE_DELETE(mStream);
        }

        return DFX_SUCCESS;
    }

    size_t OggProvider::OGG_ReadFunc(void *ptr, size_t size, size_t nMemB, void *dataSource)
    {
        OggProvider *provider = static_cast<OggProvider*>(dataSource); 
        size_t dataSize = (size_t)provider->mStream->GetSize();

        ogg_int64_t bytesRead = dataSize - provider->mOGRawPosition;
        ogg_int64_t bytesSize = size * nMemB;

        if (bytesSize < bytesRead)
            bytesRead = bytesSize;

        memcpy(ptr, (UInt8*)provider->mStream->MapStream() + provider->mOGRawPosition, (size_t)bytesRead);
        provider->mOGRawPosition += bytesRead;

        return (size_t)bytesRead;
    }

    int OggProvider::OGG_SeekFunc(void *dataSource, ogg_int64_t offset, int whence)
    {
        OggProvider *provider = static_cast<OggProvider*>(dataSource);
        size_t dataSize = (size_t)provider->mStream->GetSize();

        if (whence == SEEK_SET)
        {
            provider->mOGRawPosition = offset;
        }
        else if (whence == SEEK_CUR)
        {
            provider->mOGRawPosition += offset;
        }
        else if (whence == SEEK_END)
        {
            provider->mOGRawPosition = dataSize + offset;
        }

        if (provider->mOGRawPosition > dataSize)
            provider->mOGRawPosition = (ogg_int64_t)dataSize;

        return (size_t)provider->mOGRawPosition;
    }

    int OggProvider::OGG_CloseFunc(void *dataSource)
    {
        return 0;
    }

    long OggProvider::OGG_TellFunc(void *dataSource)
    {
        return (long)(static_cast<OggProvider*>(dataSource)->mOGRawPosition);
    }
}